//
//  Lexer.swift
//  PrattParser
//
//  Created by 神经骚栋 on 2023/9/5.
//

import Cocoa

enum LexerError: Error {
    case lexerError(message: String)
}

class Lexer: NSObject {
    
    private var code: String! = ""
    
    private var word: String? = ""
    
    private var curPos: Int = 0
    
    private var peekPos: Int = 0

    init(code: String) {
        super.init()
        self.code = code
        readCodeAction()
        let result = isDigit("8");
        print(result);
    }
    
    func readCodeAction() {
        if (peekPos >= code.count) {
            word = nil;
        } else {
            word = code.charAt(peekPos)
        }
        curPos = peekPos;
        peekPos += 1;
    }
    
    public func nextToken() throws -> Token {
        var token: Token! = PrattParser.Token(TokenType.NUM, "");
        skipWhitespace()
        switch(word) {
        case "+" :
            token = PrattParser.Token(TokenType.PLUS, "+")
            break
        case "-" :
            token = PrattParser.Token(TokenType.MINUS, "-")
            break
        case "*" :
            token = PrattParser.Token(TokenType.ASTERISK, "*")
            break
        case "/" :
            token = PrattParser.Token(TokenType.SLASH, "/")
            break
        case "(" :
            token = PrattParser.Token(TokenType.LPAREN, "(")
            break
        case ")" :
            token = PrattParser.Token(TokenType.RPAREN, ")")
            break
        case "^" :
            token = PrattParser.Token(TokenType.HAT, "^")
            break
        case nil :
            token = PrattParser.Token(TokenType.EOF, "")
            break
        default:
            if (isDigit(word)) {
                let num: String = readNum();
                token = PrattParser.Token(TokenType.NUM, num);
                return token;
            } else {
                throw LexerError.lexerError(message: "Lexer error")
            }
        }
        readCodeAction()
        return token;
    }
    
    public func isDigit(_ word: String?) -> Bool {
        if let word = word {
            return word >= "0" && word <= "9"
        } else {
            return false;
        }
    }
    
    public func skipWhitespace() {
        while (hasNext()) {
            if (word == " " || word == "\t" || word == "\n" || word == "\r")  {
                readCodeAction();
            } else {
                break
            }
        }
    }
    
    public func hasNext() -> Bool {
        return peekPos <= code.count;
    }

    
    public func readNum() -> String {
        var num: String = ""
        while (isDigit(word)) {
            num += word ?? ""
            readCodeAction()
        }
        return num;
    }
    
}



